package com.example.demo.aop;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect// 表示一个切面
@Component // 切面需要在框架启动之后 去启动 所以加上该注解
// 该类表示 切面
public class UserAspect {

    // 定义切点  使用了AspectJ表达式语法
    // *表示任意类型返回值 然后加个空格
    // com.example.demo.controller.UserController 表示拦截方法来自哪个类
    // *(..) 表示拦截所有方法
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut() {
    }


//      前置通知使⽤ @Before：通知方法会在⽬标⽅法调⽤之前执⾏。
//      后置通知使⽤ @After：通知方法会在⽬标⽅法返回或者抛出异常后调用。
//      返回之后通知使⽤ @AfterReturning：通知方法会在⽬标⽅法返回后调用
//      抛异常后通知使⽤ @AfterThrowing：通知方法会在⽬标⽅法抛出异常后调用
//      环绕通知使⽤@Around：通知包裹了被通知的⽅法，在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为
    // 前置通知
    @Before("pointcut()")
    public void doBefore() {
        System.out.println("执行了 doBefore");
    }

    @After("pointcut()")
    public void doAfter() {
        System.out.println("执行了 doAfter");
    }


    @AfterReturning("pointcut()")
    public void doAfterReturning() {
        System.out.println("执行了 doAfterReturning");
    }

//    执行了 doBefore
//    执行了 AfterThrowing
//    执行了 doAfter
//    发生异常后helloWord方法 异常之后的代码都不会执行
//
    @AfterThrowing("pointcut()")
    public void doAfterThrowing() {
        System.out.println("执行了 AfterThrowing");
    }
//    @AfterReturning 修饰的方法执行的优先级 比 @After 修饰的方法执行的优先级更高。
//      执行了 doBefore
//      执行了 say sayHi
//      执行了 doAfterReturning
//      执行了 doAfter



//    计算方法的执行时间 使用环绕通知  里面是固定写法
//    @Around("pointcut()")
//    public Object doAround(ProceedingJoinPoint joinPoint) {
//        Object obj = null;
//        System.out.println("执行了 环绕通知前置方法");
//        try {
//            obj = joinPoint.proceed();
//        }catch (Throwable e) {
//            e.printStackTrace();
//        }
//        System.out.println("执行了 环绕通知后置方法");
//        return obj;
//    }

//      执行了 环绕通知前置方法
//      执行了 doBefore
//      执行了 say sayHi
//      执行了 doAfterReturning
//      执行了 doAfter
//      执行了 环绕通知后置方法



// 计算一个方法的执行时间
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        StopWatch stopWatch = new StopWatch();// Spring 框架 里有个StopWatch方法 专门来做时间统计的


        Object obj = null;
        System.out.println("执行了 环绕通知前置方法");
        try {
            stopWatch.start();
            obj = joinPoint.proceed();// 这里表示目标方法
            stopWatch.stop();
        }catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("执行了 环绕通知后置方法");
//        System.out.println("执行花费的时间：" + stopWatch.getTotalTimeMillis() + "ms");//最好加上 执行的是什么方法
//        System.out.println(joinPoint.getSignature().getName() + "执行花费的时间：" + stopWatch.getTotalTimeMillis() + "ms");//最好加上 执行的是什么方法
        //joinPoint.getSignature().getName()在签名对象中包含了 目标方法   但是还有问题 通过项目中存在 同名的类我们又不好区分了所以
//      我们还需要打印该方法的路径 (包名＋类名)
        System.out.println(joinPoint.getSignature().getDeclaringType()
                + "."
                + joinPoint.getSignature().getName() + "执行花费的时间："
                + stopWatch.getTotalTimeMillis() + "ms");
        return obj;
    }
//      执行了 环绕通知前置方法
//      执行了 doBefore
//      执行了 say sayHi
//      执行了 doAfterReturning
//      执行了 doAfter
//      执行了 环绕通知后置方法
//      class com.example.demo.controller.UserController.sayHi执行花费的时间：11ms
}
